package com.qiu.exceldeal.core;

import com.qiu.exceldeal.ann.TitleName;
import com.qiu.exceldeal.exception.ErrorExcelFileType;
import com.qiu.exceldeal.exception.TitleNameNotFoundException;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author FireChildren
 * @create 2020/7/1 15:12
 * @Des 基于poi的excel封装    构造参数 ——>javabean类型 ，文件名.xls
 */
public class ExcelDeal<T> {

    private Class<?> clazz;
    private Workbook workbook;
    private ExcelDeal(){}
    public ExcelDeal(Class<?> clazz){

        this.clazz=clazz;
    }
    /**
     * @Des excel生成
     * 实例调用参数1:实体类的class类；2:需要生成excel的数据List
     */
    public File generate(List<T> list,File file){

        String filename = file.getName();
        //判断一下文件后缀类型，然后加载指定的类
        BolFileType(filename);

        Field[] fields = clazz.getDeclaredFields();
        Class[] fieldType = new Class[fields.length];
        //title名
        String[] title=new String[fields.length];
        //方法名String
        String[] methodname = new String[fields.length];
        //类型载入
        for (int i=0;i<fields.length;i++) {
            TitleName titleName = fields[i].getAnnotation(TitleName.class);
            if(titleName == null){
                //生成的时候没有注解就抛出异常
                throw new TitleNameNotFoundException("TitleName annotation not found,this is necessary");
            }
            //注解映射名
            if (titleName != null){ //非空代表这个属性没有添加@TitleName这个注解
                String value = titleName.value();
                title[i]=value;
                fieldType[i]= fields[i].getClass();
                //获得属性名String
                String name = fields[i].getName();
                //首字母大写化(方法化)
                String upname ="get"+name.substring(0,1).toUpperCase()+name.substring(1);
                methodname[i]=upname;
            }
        }

        CreationHelper creationHelper = workbook.getCreationHelper();
        Sheet sheet = workbook.createSheet("sheet-1");
        Row row = sheet.createRow(0);
        Cell cell=null;
        //头部内容
        for (int i = 0; i <title.length ; i++) {
            cell=row.createCell(i);
            cell.setCellValue(title[i]);
        }

        //flag计数
        int count=1;
        //获得数据
        for (T t: list) {

            Class<?> tclazz = t.getClass();
            row =sheet.createRow(count);
            //列
            int  col = 0;
            //为每一条数据执行方法
            for (String mname:methodname) {
                if(mname!=null){
                    Method m = null;
                    try {
                        m = tclazz.getDeclaredMethod(mname);
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                    Object invoke = null;
                    try {
                        invoke = m.invoke(t);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                    cell=row.createCell(col);
                    if(invoke instanceof String){
                        cell.setCellValue((String)invoke);
                    }else if(invoke instanceof Integer){
                        cell.setCellValue((Integer)invoke);
                    }else if(invoke instanceof Date){
                        CellStyle style = workbook.createCellStyle();
                        //设置单元格格式
                        style.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy年MM月dd日 HH时mm分ss秒"));
                        cell.setCellValue((Date)invoke);
                        cell.setCellStyle(style);
                    }
                    col++;
                }
            }
            count++;
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            workbook.write(fileOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }


    /**
     * @Des excel解析
     * 将excel解析成list
     */
    public List<T> analysis(File file) {
        String filename = file.getName();
        //判断一下文件后缀类型，然后加载指定的类
        BolFileType(filename);

        List<T> list = new ArrayList<>();
        Field[] field = clazz.getDeclaredFields();
        Class[] fieldtype=new Class[field.length];
        String[] setMeth = new String[field.length];
        //获得成员变量的数据类型数组
        for (int i = 0; i <field.length ; i++) {
            fieldtype[i]=field[i].getType();
            //获得属性名String
            String name = field[i].getName();
            //首字母大写化(方法化)
            String upname ="set"+name.substring(0,1).toUpperCase()+name.substring(1);
            setMeth[i]=upname;
        }
        try {
            Workbook hb= new HSSFWorkbook(new FileInputStream(file));
            //第一个表格
            Sheet sheetAt = hb.getSheetAt(0);
            //列表最后行的序号
            int lastindex=sheetAt.getLastRowNum();
            //去掉title，从第二行开始算
            for (int i = 1; i <=lastindex ; i++) {
                Row row= sheetAt.getRow(i);
                //获得最后列的序号
                int lastCellNum = row.getLastCellNum();

                T t =(T) clazz.newInstance();
                for (int j = 0; j <lastCellNum ; j++) {
                    Cell cell =row.getCell(j);
                    //获取单元中数据的数据类型
                    int cellType = cell.getCellType();
                    //寻找数据类型并执行对应的处理
                    selTypeAndPutInObj(cellType,cell,clazz,setMeth[j],fieldtype[j],t);
                }
                list.add(t);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     *  excel文件兼容性判断
     * @param fileName
     */
    private void BolFileType(String fileName){
        String suffix = fileName.split("\\.")[1];
        if("xls".equals(suffix)){
            workbook=new HSSFWorkbook();
        }else if("xlsx".equals(suffix)){
            workbook=new XSSFWorkbook();
        }else {
            throw new ErrorExcelFileType("Invalid excel file type");
        }
    }


    /**
     * 放入对象中类型选择
     * @param cellType
     * @param cell
     * @param clazz
     * @param methname
     * @param typename
     * @param t
     */
    private void selTypeAndPutInObj(int cellType,Cell cell,Class<?> clazz,String methname,Class<?>  typename,T t){
        switch (cellType){
            //String类型
            case 1:{
                String stringval = cell.getStringCellValue();
                Method declaredMethod = null;
                    try {
                        declaredMethod = clazz.getDeclaredMethod(methname,typename);
                        declaredMethod.invoke(t,stringval);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                break;
            }
            //数字double类型   Date类型转化到java里也是double
            case 0:{
                Method declaredMethod = null;
                    try {
                        declaredMethod = clazz.getDeclaredMethod(methname,typename);
                        //为int型
                        if(typename == Integer.class){
                            int intval = (int)cell.getNumericCellValue();
                            declaredMethod.invoke(t,intval);
                        //为Date类型
                        }else if(typename == Date.class){
                            Date dateCellValue = cell.getDateCellValue();
                            declaredMethod.invoke(t,dateCellValue);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                break;
            }
        }
    }
}
